﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Net;
using System.IO.Ports;

namespace Svr
{
    delegate void Writer (string msg);

    class Program
    {
        static Thread           _t1         = null;
        static Thread           _t2         = null;
        static Writer           _writer     = writerVacio;
        static AutoResetEvent   _eventoFin  = new AutoResetEvent(false);
        static object           _lockWriter = new object();
        static object           _lockConsole= new object();

        static void writerVacio (string s) { }

        public static void writer (string s) 
        {
            lock (_lockWriter) {
                _writer(s);
            }

            #if DEBUG
            Thread.Sleep(1);
            #endif
        }

        static void Main (string [] args)
        {
            TcpListener listener = new TcpListener(1337);
            listener.Start();

            iniciarHilos();

            for (;;)
            {
                lock (_lockConsole)
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine("Escuchando");
                }

                using (TcpClient tc = listener.AcceptTcpClient())
                {
                    tc.NoDelay = true; // no nagle

                    using (NetworkStream ns = tc.GetStream())
                    {
                        _writer = (s) => 
                        {
                            try
                            {
                                ns.Write(Cmd.mkStringCmd(s));
                                ns.Write("!");

                                parseResponse(ns);
                            }
                            catch (Exception exx)
                            {
                                show(exx.Message, ConsoleColor.Red);
                                _writer = writerVacio;
                                ns.Close();
                                tc.Close();
                                _eventoFin.Set();
                            }
                        };
    
                        _eventoFin.WaitOne();
                    } // using NetworkStream ns
                } // using TcpClient
            } // loop eterno for(;;)
        }

        private static void parseResponse (NetworkStream ns)
        {
            for (; ; )
            {
                int @byte = ns.ReadByte();

                if (@byte == '!')
                {
                    break;
                }
                else if (@byte == '*')
                {
                    if (! executeCmd(ns, Cmd.parseCmd(ns)))
                        break;
                }
                else
                {
                    throw new Exception(
                        string.Format("unknown byte '{0}' in response", @byte)
                    );
                }
            }
        }

        private static bool executeCmd(NetworkStream ns, List<byte[]> cmd)
        {
            string cmdFirst = Encoding.ASCII.GetString(cmd[0]);

            // se pueden inyectar llamadas a parseResponse(ns) recursivas.
            // :D

            switch (cmdFirst)
            {
                case "fin":
                    _writer = writerVacio;
                    _eventoFin.Set();
                    // necesito ROMPER el bucle que ejecuta los comandos
                    return false;
                case "culo":
                    // saludo de culo
                    Console.WriteLine("hola soy un culo");
                    break;
                case "concha":
                    // saludo de concha
                    Console.WriteLine("hola soy una concha");
                    break;
                case "getstack":
                    // devolveria el stack trace de un hilo
                    break;
                case "getfullstack":
                    // devolveria todo el stacktrace (de todos los hilos)
                    break;
                default:
                    throw new Exception(
                        string.Format("unknown '{0}' command", cmdFirst)
                    );
            }

            return true;
        }

        static void show (string s, ConsoleColor cc)
        {
            lock (_lockConsole)
            {
                Console.ForegroundColor = cc;
                Console.WriteLine(s);
            }
        }

        static void iniciarHilos()
        {
            _t1 = new Thread(new ThreadStart(
                () =>
                {
                    for (; ; )
                    {
                        writer("func");
                    }
                }));

            _t2 = new Thread(new ThreadStart(
                () =>
                {
                    for (; ; )
                    {
                        writer("tick");
                        Thread.Sleep(100);
                    }
                }));

            _t1.Start();
            _t2.Start();
        }
    }
}
